﻿/// <reference path="_namespace.js" />
/// <reference path="/ajax/math/Matrix.js"/>
/// <reference path="/ajax/math/Plane3.js"/>
/// <reference path="/ajax/math/Ray3.js"/>
/// <reference path="PlaneIntersectionType.js"/>
/// <reference path="ContainmentType.js"/>
/// <reference path="BoundingBox.js"/>
/// <reference path="BoundingSphere.js"/>
/// <reference path="/ajax/types/Float.js"/>

/*****************************************************************************
Ajaxna library class definition.
Author: krobinson 11/5/2008 1:24:03 AM.
Class:  ajax.xna.framework.BoundingFrustum
*****************************************************************************/

$addNamespace("ajax.xna.framework");
$imports("ajax.math.Matrix", "ajax.math.Vector3", "ajax.math.Plane3", "ajax.math.Ray3");
$imports("ajax.xna.framework.PlaneIntersectionType");
$imports("ajax.xna.framework.ContainmentType");
$imports("ajax.types.Float");

//-----------------------------------------------------------------------------

ajax.xna.framework.BoundingFrustum = function(matrix)
{///<param name="matrix" type="ajax.math.Matrix">Combined matrix that usually takes view × projection matrix.</param>    

    this._planes = new Array();
    // New up Planes (not value types)
    for (var i = 0; i < this.planesCount; i++)
        this._planes[i] = new ajax.math.Plane3(new ajax.math.Vector3(), new ajax.math.Vector3());

    this._cornerArray = new Array();
    // New up corner array (not value types)
    for (var i = 0; i < this.cornerCount; i++)
        this._cornerArray[i] = new ajax.math.Vector3();

    this._setMatrix(matrix);

}
$extend("ajax.xna.framework.BoundingFrustum", "");
//-----------------------------------------------------------------------------

// Private fields
ajax.xna.framework.BoundingFrustum.prototype._bottomPlaneIndex = 5;
ajax.xna.framework.BoundingFrustum.prototype._cornerArray = [];
ajax.xna.framework.BoundingFrustum.prototype._farPlaneIndex = 1;
ajax.xna.framework.BoundingFrustum.prototype._leftPlaneIndex = 2;
ajax.xna.framework.BoundingFrustum.prototype._matrix = ajax.math.Matrix.prototype;
ajax.xna.framework.BoundingFrustum.prototype._nearPlaneIndex = 0;
ajax.xna.framework.BoundingFrustum.prototype._numPlanes = 6;
ajax.xna.framework.BoundingFrustum.prototype._planes = [];
ajax.xna.framework.BoundingFrustum.prototype._rightPlaneIndex = 3;
ajax.xna.framework.BoundingFrustum.prototype._topPlaneIndex = 4;

// Private static methods
//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum._computeIntersectionLine = function(p1, p2)
{///<param name="p1" type="ajax.math.Plane3"/>
    ///<param name="p2" type="ajax.math.Plane3"/>
    ///<returns type="ajax.math.Ray3"/>
    //p1 = ajax.math.Plane3.cast(p1); p2 = ajax.math.Plane3.cast(p2);

    var Vector3 = ajax.math.Vector3;
    var direction = Vector3.cross(p1.normal, p2.normal);
    var num = direction.lengthSq();

    var position = Vector3.cross(Vector3.add(Vector3.multiplyScalar(p2.normal, -p1.distance), Vector3.multiplyScalar(p1.normal, p2.distance)), direction) / num;
    //ray.Position = (Vector3) (Vector3.Cross((Vector3) ((-p1.D * p2.Normal) + (p2.D * p1.Normal)), ray.Direction) / num);
    var ray = new ajax.math.Ray3(position, direction);
    return ray;
}
//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum._computeIntersection = function(plane, ray)
{///<param name="plane" type="ajax.math.Plane3"/>
    ///<param name="ray" type="ajax.math.Ray3"/>
    ///<returns type="ajax.math.Vector3"/>
    var Vector3 = ajax.math.Vector3;
    //plane = ajax.math.Plane3.cast(plane);
    //ray   = ajax.math.Ray3.cast(ray);
    var num = (-plane.distance - Vector3.dot(plane.normal, ray.normal)) / Vector3.dot(plane.normal, ray.normal);

    var result = Vector3.add(ray.origin, Vector3.multiplyScalar(ray.normal, num));
    return result;
}

ajax.xna.framework.BoundingFrustum.prototype.cornerCount = 8;
ajax.xna.framework.BoundingFrustum.prototype.planesCount = 6;

// Private methods
//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum.prototype._setMatrix = function(value)
{///<param name="value" type="ajax.math.Matrix"/>
    //value = ajax.math.Matrix.cast(value);        

    this._matrix = value;
    this._planes[2].normal.x = -value.m[0][3] - value.m[0][0]; // -M14 - value.M11
    this._planes[2].normal.y = -value.m[1][3] - value.m[1][0]; // -M24 - value.M21;
    this._planes[2].normal.z = -value.m[2][3] - value.m[2][0]; //-M34 - value.M31;
    this._planes[2].distance = -value.m[3][3] - value.m[3][1]; //-M44 - value.M41;

    this._planes[3].normal.x = -value.m[0][3] + value.m[0][0]; //-M14 + value.M11;
    this._planes[3].normal.y = -value.m[1][3] + value.m[1][0]; //-M24 + value.M21;
    this._planes[3].normal.z = -value.m[2][3] + value.m[2][0]; // -M34 + value.M31;    
    this._planes[3].distance = -value.m[3][3] + value.m[3][0]; // -M44 + value.M41;

    this._planes[4].normal.x = -value.m[0][3] + value.m[0][1]; // -M14 + value.M12;
    this._planes[4].normal.y = -value.m[1][3] + value.m[1][1]; // -M24 + value.M22;
    this._planes[4].normal.z = -value.m[2][3] + value.m[2][1]; // -M34 + value.M32;
    this._planes[4].distance = -value.m[3][3] + value.m[3][1]; // -M44 + value.M42;

    this._planes[5].normal.x = -value.m[0][3] - value.m[0][1]; // -M14 - value.M12;
    this._planes[5].normal.y = -value.m[1][3] - value.m[1][1]; // -M24 - value.M22;
    this._planes[5].normal.z = -value.m[2][3] - value.m[2][1]; // -M34 - value.M32;
    this._planes[5].distance = -value.m[3][3] - value.m[3][1]; // -M44 - value.M42;

    this._planes[0].normal.x = -value.m[0][2]; // -M13;
    this._planes[0].normal.y = -value.m[1][2]; // -M23;
    this._planes[0].normal.z = -value.m[2][2]; // -M33;
    this._planes[0].distance = -value.m[3][2]; // -M43;    

    this._planes[1].normal.x = -value.m[0][3] + value.m[0][2]; // -M14 + value.M13;
    this._planes[1].normal.y = -value.m[1][3] + value.m[1][2]; // -M24 + value.M23;
    this._planes[1].normal.z = -value.m[2][3] + value.m[2][2]; // -M34 + value.M33;
    this._planes[1].distance = -value.m[3][3] + value.m[3][2]; // -M44 + value.M43;
    var plane;
    for (var i = 0; i < this.planesCount; i++)
    {
        //var plane = ajax.math.Plane3.cast(this._planes[i]);
        plane = this._planes[i];
        var num2 = plane.normal.length();
        plane.normal.divideScalar(num2);
        plane.distance /= num2;
        /*
        this._planes[i].Normal = (Vector3) (this._planes[i].Normal / num2);
        this._planes[i].D /= num2;
        */
    }
    var BoundingFrustum = ajax.xna.framework.BoundingFrustum;
    var ray = BoundingFrustum._computeIntersectionLine(this._planes[0], this._planes[2]);
    this._cornerArray[0] = BoundingFrustum._computeIntersection(this._planes[4], ray);
    this._cornerArray[3] = BoundingFrustum._computeIntersection(this._planes[5], ray);

    ray = BoundingFrustum._computeIntersectionLine(this._planes[3], this._planes[0]);
    this._cornerArray[1] = BoundingFrustum._computeIntersection(this._planes[4], ray);
    this._cornerArray[2] = BoundingFrustum._computeIntersection(this._planes[5], ray);

    ray = BoundingFrustum._computeIntersectionLine(this._planes[2], this._planes[1]);
    this._cornerArray[4] = BoundingFrustum._computeIntersection(this._planes[4], ray);
    this._cornerArray[7] = BoundingFrustum._computeIntersection(this._planes[5], ray);

    ray = BoundingFrustum._computeIntersectionLine(this._planes[1], this._planes[3]);
    this._cornerArray[5] = BoundingFrustum._computeIntersection(this._planes[4], ray);
    this._cornerArray[6] = BoundingFrustum._computeIntersection(this._planes[5], ray);
}
// Public members
//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum.prototype.cornerCount = 8;

ajax.xna.framework.BoundingFrustum.prototype.get_bottom = function()
{///<value type="ajax.math.Plane3">Gets the bottom plane of the frustum.</value>
    return this._planes[5];
}

ajax.xna.framework.BoundingFrustum.prototype.get_far = function()
{///<value type="ajax.math.Plane3">Gets the far plane of the frustum.</value>
    return this._planes[1];
}

ajax.xna.framework.BoundingFrustum.prototype.get_left = function()
{///<value type="ajax.math.Plane3">Gets the left plane of the frustum.</value>
    return this._planes[2];
}

ajax.xna.framework.BoundingFrustum.prototype.get_near = function()
{///<value type="ajax.math.Plane3">Gets the near plane of the frustum.</value>
    return this._planes[0];
}

ajax.xna.framework.BoundingFrustum.prototype.get_right = function()
{///<value type="ajax.math.Plane3">Gets the right plane of the frustum.</value>
    return this._planes[3];
}

ajax.xna.framework.BoundingFrustum.prototype.get_top = function()
{///<value type="ajax.math.Plane3">Gets the top plane of the frustum.</value>
    return this._planes[4];
}

ajax.xna.framework.BoundingFrustum.prototype.get_matrix = function()
{///<value type="ajax.math.Matrix">Gets or sets the Matrix that was used to instantiate this bounding frustum.</value>
    return this._matrix;
}
ajax.xna.framework.BoundingFrustum.prototype.set_matrix = function(value)
{
    this._setMatrix(value);
}




// Public Methods

/* Not Implemented... :-(
public bool Intersects(BoundingBox box);
public bool Intersects(BoundingFrustum frustum);
public bool Intersects(BoundingSphere sphere);
public void Intersects(ref BoundingBox box, out bool result);
public void Intersects(ref BoundingSphere sphere, out bool result);
public ContainmentType Contains(BoundingFrustum frustum);

internal void SupportMapping(ref Vector3 v, out Vector3 result);
*/




//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum.prototype.containsBoundingBox = function(box)
{///<param name="box" type"ajax.xna.framework.BoundingBox"/>
    ///<returns type="ajax.xna.framework.ContainmentType"/>
    //box = ajax.xna.framework.BoundingBox.cast(box);

    var PlaneIntersectionType = ajax.xna.framework.PlaneIntersectionType;
    var ContainmentType = ajax.xna.framework.ContainmentType;

    var flag = false;
    for (var i = 0; i < this._planes.length; i++)
    {
        switch (box.intersectsPlane(this._planes[i]))
        {
            case PlaneIntersectionType.Front:
                return ContainmentType.Disjoint;

            case PlaneIntersectionType.Intersecting:
                flag = true;
                break;
        }
    }
    if (!flag)
        return ContainmentType.Contains;

    return ContainmentType.Intersects;
}

//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum.prototype.sphereIsInside = function(sphere)
{///<param name="sphere" type="ajax.xna.framework.BoundingSphere"/>
    ///<returns type="boolean">Returns true if any part of the sphere is inside the frustum.</returns>
    var plane;
    for (var i = 0; i < this._planes.length; i++)
    {
        plane = ajax.math.Plane3.cast(this._planes[i]);
        if (plane.intersectsBoundingSphere(sphere) == ajax.xna.framework.PlaneIntersectionType.Front)
            return false;
    }
    return true;
}

//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum.prototype.containsBoundingSphere = function(sphere)
{///<param name="sphere" type="ajax.xna.framework.BoundingSphere"/>
    ///<returns type="ajax.xna.framework.ContainmentType"/>

    var center = sphere.center;
    var radius = sphere.radius;

    var num2 = 0;
    var plane;
    for (var i = 0; i < this._planes.length; i++)
    {
        //plane = ajax.math.Plane3.cast(this._planes[i]);
        plane = this._planes[i];
        var num5 = ((plane.normal.x * center.x) + (plane.normal.y * center.y)) + (plane.normal.z * center.z);
        var num3 = num5 + plane.distance;
        if (num3 > radius)
        {
            return ajax.xna.framework.ContainmentType.Disjoint;
        }
        if (num3 < -radius)
        {
            num2++;
        }
    }
    if (num2 != 6)
    {
        return ajax.xna.framework.ContainmentType.Intersects;
    }
    return ajax.xna.framework.ContainmentType.Contains;
}

//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum.prototype.containsVector3 = function(point)
{///<summary>Checks whether the current BoundingFrustum contains the specified point. </summary>
    ///<param name="point" type="ajax.math.Vector3"/>
    ///<returns type="ajax.xna.framework.ContainmentType"/>
    //point = ajax.math.Vector3.cast(point);

    var plane;
    for (var i = 0; i < this._planes.length; i++)
    {
        //plane = ajax.math.Plane3.cast(this._planes[i]);
        plane = this._planes[i];
        var num2 = (((plane.normal.x * point.x) + (plane.normal.y * point.y)) + (plane.normal.z * point.z)) + plane.distance;
        if (num2 > 1E-05)
            return ajax.xna.framework.ContainmentType.Disjoint;
    }
    return ajax.xna.framework.ContainmentType.Contains;
}

//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum.prototype.intersectsRay3 = function(ray)
{///<summary>Checks whether the current BoundingFrustum intersects the specified Ray.</summary> 
    ///<param name="ray" type="ajax.math.Ray3"/>
    ///<returns type="float" mayBeNull="true"/>

    var Vector3 = ajax.math.Vector3;
    //ray = ajax.math.Ray3.cast(ray);
    var type = this.containsVector3(ray.origin);

    if (type == ajax.xna.framework.ContainmentType.Contains)
    {
        return 0;
    }
    else
    {
        var minValue = ajax.types.Float.minValue;
        var maxValue = ajax.types.Float.maxValue;
        var plane;
        for (var i = 0; i < this._planes.length; i++)
        {
            //plane = ajax.math.Plane3.cast(this._planes[i]);
            plane = this._planes[i];
            var num6 = Vector3.dot(ray.normal, plane.normal);
            var num3 = Vector3.dot(ray.origin, plane.normal);

            num3 += plane.distance;

            if (Math.abs(num6) < 1E-05)
            {
                if (num3 > 0)
                {
                    return 0;
                }
            }
            else
            {
                var num = -num3 / num6;
                if (num6 < 0)
                {
                    if (num > maxValue)
                    {
                        return 0;
                    }
                    if (num > minValue)
                    {
                        minValue = num;
                    }
                }
                else
                {
                    if (num < minValue)
                    {
                        return 0;
                    }
                    if (num < maxValue)
                    {
                        maxValue = num;
                    }
                }
            }
        } // end planes loop

        var num7 = (minValue >= 0) ? minValue : maxValue;
        return num7;
    } // end else
    return 0;
}

//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum.prototype.intersectsPlane3 = function(plane)
{///<summary>Checks whether the current BoundingFrustum intersects the specified Plane.</summary>
    ///<param name="plane" type="ajax.math.Plane3"/>
    ///<returns type="ajax.xna.framework.PlaneIntersectionType"/>    
    //plane = ajax.math.Plane3.cast(plane);
    var Vector3 = ajax.math.Vector3;
    var num = 0;
    for (var i = 0; i < this.cornerCount; i++)
    {
        var num3 = Vector3.dot(this._cornerArray[i], plane.normal);

        if ((num3 + plane.distance) > 0)
        {
            num |= 1;
        }
        else
        {
            num |= 2;
        }
        if (num == 3)
        {
            return ajax.xna.framework.PlaneIntersectionType.Intersecting;
        }
    }
    if (num != 1)
    {
        return ajax.xna.framework.PlaneIntersectionType.Back;
    }
    return ajax.xna.framework.PlaneIntersectionType.Front;
}

//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum.prototype.getCorners = function()
{///<summary>Gets an array of points that make up the corners of the BoundingFrustum. </summary>
    return this._cornerArray.copy();
}

//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum.prototype.toString = function()
{
    return String.toFormat("Near:{0} \rFar:{1} \rLeft:{2} \rRight:{3} \rTop:{4} \rBottom:{5}",
        this.get_near(), this.get_far(), this.get_left(), this.get_right(), this.get_top(), this.get_bottom()
    );
}


// Statics
//-----------------------------------------------------------------------------
ajax.xna.framework.BoundingFrustum.cast = function(obj)
{///<returns type="ajax.xna.framework.BoundingFrustum"/>
    return obj;
}